package p.ithorns.framework.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * SystemUtil
 *
 * 系统工具
 * 鉴别当前操作系统类型
 *
 *
 * @author ithorns
 * @version 1.0
 * @date 2020/5/15 14:02
 */
public class SystemUtil {

    private final static Logger log = LoggerFactory.getLogger(SystemUtil.class);

    private static final String OS = System.getProperty("os.name").toLowerCase();

    private final static String SN = getSn();

    /**
     * 获取CPUID、硬盘序列号、MAC地址、主板序列号
     *
     * @return String
     */
    public static String getSn() {
        if (StringUtil.isNotBlank(SN)) {
            return SN;
        }

        StringBuilder deviceNo = new StringBuilder();

        //System.out.println("===============================================================");
        //System.out.println("Generating Device SN for OS code:  " +  getOsName());

        log.info("==================================================================");
        log.info("Generating Device SN for OS code: {}", getOsName());

        if (isLinux()) {

            /**
             * linux
             * cpuid : dmidecode -t processor | grep 'ID'
             * mainboard : dmidecode |grep 'Serial Number'
             * disk : fdisk -l
             * mac : ifconfig -a
             */
            String cpuid = getSerialNumber("dmidecode -t processor | grep 'ID'", "ID", ":");
            log.debug("CPU ID: [{}]", cpuid);

            String serialNumber = getSerialNumber("dmidecode -s system-serial-number");
            //getSerialNumber("dmidecode |grep 'Serial Number'", "Serial Number", ":");
            log.debug("System Serial Number: [{}]", serialNumber);

            //String diskNumber = getSerialNumber("fdisk -l", "Disk identifier", ":");
            //log.debug("Disk Number: {}", diskNumber);

            String mac = getMac();
            //getSerialNumber("ifconfig -a", "ether", " ");
            log.debug("Local Mac: [{}]", mac);

            String oem = getSerialNumber("dmidecode -t 11 | grep 'String 1' ", "String 1", ":");
            log.debug("OEM Info: [{}]", oem);

            deviceNo.append(cpuid)
                    .append(serialNumber)
                    .append(mac)
                    .append(oem);

        } else if (isWindows()) {
            String cpuid = getCPUSerial();
            String mainboard = getMainboardSN();
            String disk = getHardDiskSN();
            String mac = getMac();

            log.debug("CPU SN: {}", cpuid);
            log.debug("MainBoard SN: {}", mainboard);
            log.debug("Disk SN:{}", disk);
            log.debug("MAC SN: {}", mac);

            deviceNo.append(cpuid)
                    .append(mainboard)
                    .append(disk);

        } else {
            log.warn("The OS is not supported.");
            throw new UnsupportedOperationException();
        }

        // 可控制功能范围：使用周期+最多可部署基站数量+最多可使用标签卡数量+最多可部署信标数
        // 数据格式：128字符(md5加密结果) + 4字符(mac地址开始符)+mac地址数据+4字符(mac地址结束符)+

        String md5 = DigestUtils.md5DigestAsHex(deviceNo.toString().getBytes());

        log.info("Device SN was generated ==> [{}]", md5);
        log.info("==================================================================");
        // System.out.println("Device SN was generated ==> ["+ md5+ "]");
        //System.out.println("===============================================================");

        return md5;
    }

    /**
     * MainboardSN
     *
     * @return String
     */
    private static String getMainboardSN() {
        String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
                + "Set colItems = objWMIService.ExecQuery _ \n"
                + "   (\"Select * from Win32_BaseBoard\") \n"
                + "For Each objItem in colItems \n"
                + "    Wscript.Echo objItem.SerialNumber \n"
                + "    exit for  ' do the first cpu only! \n" + "Next \n";
        return readDeviceNo(vbs);
    }

    /**
     * HardDiskSN
     *
     * @return String
     */
    private static String getHardDiskSN() {
        String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
                + "Set colItems = objWMIService.ExecQuery _ \n"
                + "   (\"Select * from Win32_DiskDrive\") \n"
                + "For Each objItem in colItems \n"
                + "    Wscript.Echo objItem.SerialNumber \n"
                + "    exit for  ' do the first cpu only! \n" + "Next \n";
        return readDeviceNo(vbs);
    }


    /**
     * 获取CPU序列号
     *
     * @return String
     */
    private static String getCPUSerial() {
        String vbs = "Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
                + "Set colItems = objWMIService.ExecQuery _ \n"
                + "   (\"Select * from Win32_Processor\") \n"
                + "For Each objItem in colItems \n"
                + "    Wscript.Echo objItem.ProcessorId \n"
                + "    exit for  ' do the first cpu only! \n" + "Next \n";

        String result = readDeviceNo(vbs);
        if (!StringUtils.hasText(result)) {
            result = "无CPU_ID被读取";
        }
        return result;
    }

    private static List<String> getLocalHostLANAddress() throws UnknownHostException, SocketException {
        List<String> ips = new ArrayList<String>();
        Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces();
        while (interfs.hasMoreElements()) {
            NetworkInterface interf = interfs.nextElement();
            Enumeration<InetAddress> addres = interf.getInetAddresses();
            while (addres.hasMoreElements()) {
                InetAddress in = addres.nextElement();
                if (in instanceof Inet4Address) {
                    log.debug("v4:" + in.getHostAddress());
                    if (!"127.0.0.1".equals(in.getHostAddress())) {
                        ips.add(in.getHostAddress());
                    }
                }
            }
        }
        return ips;
    }

    /**
     * MAC
     *
     * @return String
     */
    private static String getMac() {
        try {
            StringBuilder resultStr = new StringBuilder();
            List<String> ls = getLocalHostLANAddress();
            for (String str : ls) {
                InetAddress ia = InetAddress.getByName(str);
                byte[] mac = NetworkInterface.getByInetAddress(ia).getHardwareAddress();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < mac.length; i++) {
                    if (i != 0) {
                        sb.append("-");
                    }
                    String s = Integer.toHexString(mac[i] & 0xFF);
                    sb.append(s.length() == 1 ? 0 + s : s);
                }
                resultStr.append(sb.toString().toUpperCase()).append(",");
            }
            return resultStr.toString();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

    private static String readDeviceNo(String vbs) {
        StringBuilder result = new StringBuilder();
        BufferedReader input = null;
        try {
            File file = File.createTempFile("realhowto", ".vbs");
            file.deleteOnExit();
            FileWriter fw = new FileWriter(file);
            fw.write(vbs);
            fw.close();
            String path = file.getPath().replace("%20", " ");
            Process p = Runtime.getRuntime().exec("cscript //NoLogo " + path);
            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = input.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return StringUtils.trimAllWhitespace(result.toString());

    }

    /***************************linux*********************************/

    private static String executeLinuxCmd(String cmd) {

        Process process = null;
        try {
            process = Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            log.warn("The command failed to execute: [{}]", cmd);
            return null;
        }

        if (process == null) {
            return null;
        }

        try (
                InputStream in = process.getInputStream();
                BufferedReader bs = new BufferedReader(new InputStreamReader(in));
        ) {
            // log.debug("Got cmd job: {}", cmd);
            StringBuffer out = new StringBuffer();
            byte[] b = new byte[2048];
            for (int n; (n = in.read(b)) != -1; ) {
                out.append(new String(b, 0, n));
            }

            process.destroy();
            return out.toString();
        } catch (Exception e) {
            log.warn(e.getMessage());
        }

        return null;
    }

    /**
     * @param cmd    命令语句
     * @param record 要查看的字段
     * @param symbol 分隔符
     * @return String
     */
    private static String getSerialNumber(String cmd, String record, String symbol) {
        String execResult = executeLinuxCmd(cmd);

        if (!StringUtils.hasText(execResult)) {
            return "";
        }

        String[] infos = execResult.split("\n");

        for (String info : infos) {
            if (info.contains(record)) {
                info = StringUtils.trimAllWhitespace(info);
                String[] sn = info.split(symbol);
                if (sn.length > 1) {
                    return sn[1];
                }
            }
        }

        return "";
    }

    private static String getSerialNumber(String cmd) {
        String result = executeLinuxCmd(cmd);
        return StringUtil.trimAllWhitespace(result);
    }

    /**
     * 平台
     */
    public static enum EPlatform {
        // any
        Any("any"),
        Linux("Linux"),
        Mac_OS("Mac OS"),
        Mac_OS_X("Mac OS X"),
        Windows("Windows"),
        OS2("OS/2"),
        Solaris("Solaris"),
        SunOS("SunOS"),
        MPEiX("MPE/iX"),
        HP_UX("HP-UX"),
        AIX("AIX"),
        OS390("OS/390"),
        FreeBSD("FreeBSD"),
        Irix("Irix"),
        Digital_Unix("Digital Unix"),
        NetWare_411("NetWare"),
        OSF1("OSF1"),
        OpenVMS("OpenVMS"),
        Others("Others");

        private EPlatform(String desc) {
            this.description = desc;
        }

        @Override
        public String toString() {
            return description;
        }

        private String description;
    }



    public static String getOsName() {
        return OS;
    }

    public static boolean isLinux() {
        return null != OS && OS.contains("linux");
    }

    public static boolean isMacOs() {
        return OS.contains("mac") && OS.indexOf("os") > 0 && !OS.contains("x");
    }

    public static boolean isMacOsx() {
        return OS.contains("mac") && OS.indexOf("os") > 0 && OS.indexOf("x") > 0;
    }

    public static boolean isWindows() {
        return null != OS && OS.contains("windows");
    }

    public static boolean isOscontains2() {
        return OS.contains("os/2");
    }

    public static boolean isSolaris() {
        return OS.contains("solaris");
    }

    public static boolean isSunOs() {
        return OS.contains("sunos");
    }

    public static boolean isMpeix() {
        return OS.contains("mpe/ix");
    }

    public static boolean isHpux() {
        return OS.contains("hp-ux");
    }

    public static boolean isAix() {
        return OS.contains("aix");
    }

    public static boolean isOs390() {
        return OS.contains("os/390");
    }

    public static boolean isFreeBsd() {
        return OS.contains("freebsd");
    }

    public static boolean isIrix() {
        return OS.contains("irix");
    }

    public static boolean isDigitalUnix() {
        return OS.contains("digital") && OS.indexOf("unix") > 0;
    }

    public static boolean isNetWare() {
        return OS.contains("netware");
    }

    public static boolean isOSF1() {
        return OS.contains("osf1");
    }

    public static boolean isOpenVms() {
        return OS.contains("openvms");
    }
}
